////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camximagebuffer.cpp
///
/// @brief Image Buffer implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxatomic.h"
#include "camxutils.h"
#include "camxcsl.h"
#include "camxhwenvironment.h"
#include "camxsettingsmanager.h"
#include "camximagebuffer.h"
#include "camximageformatutils.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::ImageBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ImageBuffer::ImageBuffer(
    ImageBufferManager* pImageBufferManager)
{
    m_pImageBufferManager = pImageBufferManager;

    // By default it is 1 i.e. HFR disabled. If HFR is enabled it will be set to some value greater than 1 when the memory is
    // allocated during Allocate()
    m_numBatchedFrames = 1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::~ImageBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ImageBuffer::~ImageBuffer()
{
    if (TRUE == m_isBacked)
    {
        Release();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::Allocate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ImageBuffer::Allocate(
    const CHAR*           pStr,
    const ImageFormat*    pFormat,
    UINT32                flags,
    const INT32*          pDeviceIndices,
    UINT                  deviceCount,
    UINT                  numBatchedFrames)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(FALSE == m_isBacked);
    if (TRUE == m_isBacked)
    {
        result = CamxResultEInvalidState;
    }

    CAMX_ASSERT(NULL != pFormat);
    if (NULL == pFormat)
    {
        result = CamxResultEInvalidArg;
    }

    if (CamxResultSuccess == result)
    {
        m_numBatchedFrames = numBatchedFrames;

        CAMX_ASSERT(numBatchedFrames > 0);

        const SIZE_T totalBufferSizeBytes = (ImageFormatUtils::GetTotalSize(pFormat) * numBatchedFrames);
        CSLBufferInfo bufferInfo;

        const StaticSettings* pSettings = HwEnvironment::GetInstance()->GetStaticSettings();

        if ((TRUE == pSettings->autoImageDump) || (TRUE == pSettings->dynamicImageDump) || (TRUE == pSettings->watermarkImage))
        {
            flags |= CSLMemFlagUMDAccess;
        }

        // Allocate all memory in a single allocation
        /// @todo (CAMX-954) Does alignment need to be handled separately for numBatchedFrames > 1?
        result = CSLAlloc(pStr, &bufferInfo, totalBufferSizeBytes, pFormat->alignment, flags, pDeviceIndices, deviceCount);
        // Only update internal state if success
        if (CamxResultSuccess == result)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupUtils, "Image Buffer %x CSLAlloc returned bufferInfo.fd=%d handle = %d",
                this, bufferInfo.fd, bufferInfo.hHandle);

            result = ImportCSLBuffer(pFormat, &bufferInfo);
            if (CamxResultSuccess == result)
            {
                m_format = *pFormat;
            }
            else
            {
                CSLReleaseBuffer(bufferInfo.hHandle);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::Import
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ImageBuffer::Import(
    const ImageFormat*  pFormat,
    const NativeHandle* phNativeBuffer,
    SIZE_T              offset,
    SIZE_T              size,
    UINT32              flags,
    const INT32*        pDeviceIndices,
    UINT                deviceCount)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(NULL != pFormat);
    CAMX_ASSERT(TRUE != m_isBacked);
    if (NULL == pFormat)
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Invalid argument");
    }
    else if (TRUE == m_isBacked)
    {
        result = CamxResultEInvalidState;
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Called import with memory allocated internally");
    }

    if (CamxResultSuccess == result)
    {
        CSLBufferInfo   bufferInfo;
        const StaticSettings* pSettings = HwEnvironment::GetInstance()->GetStaticSettings();

        if ((TRUE == pSettings->autoImageDump)    ||
            (TRUE == pSettings->dynamicImageDump) ||
            (TRUE == pSettings->reprocessDump)    ||
            (TRUE == pSettings->watermarkImage))
        {
            flags |= CSLMemFlagUMDAccess;
        }

        result = CSLMapNativeBuffer(&bufferInfo,
                                    reinterpret_cast<const CSLNativeHandle*>(phNativeBuffer),
                                    offset,
                                    size,
                                    flags,
                                    pDeviceIndices,
                                    deviceCount);
        // Only update internal state if success
        if (CamxResultSuccess == result)
        {
            result = ImportCSLBuffer(pFormat, &bufferInfo);
            if (CamxResultSuccess == result)
            {
                m_format            = *pFormat;
                m_phNativeBuffer    = phNativeBuffer;
            }
            else
            {
                CSLReleaseBuffer(bufferInfo.hHandle);
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupUtils, "CSLMapNativeBuffer failed with %d", result);
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::ImportCSLBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ImageBuffer::ImportCSLBuffer(
    const ImageFormat* pFormat,
    const CSLBufferInfo* pBufferInfo)
{
    CamxResult  result              = CamxResultEResource;
    SIZE_T      requiredBufferSize  = ImageFormatUtils::GetTotalSize(pFormat);
    UINT        numberOfPlanes      = ImageFormatUtils::GetNumberOfPlanes(pFormat);

    CAMX_ASSERT(pBufferInfo->size >= requiredBufferSize);

    if (pBufferInfo->size >= requiredBufferSize)
    {
        m_hBuffer               = pBufferInfo->hHandle;
        m_pVirtualAddr          = static_cast<BYTE*>(pBufferInfo->pVirtualAddr);
        m_fileDescriptor        = pBufferInfo->fd;
        m_planeStartOffset[0]   = 0;

        for (UINT i = 0; i < numberOfPlanes; i++)
        {
            m_planeSize[i] = ImageFormatUtils::GetPlaneSize(pFormat, i);

            // All planes reside one after the other i.e. all Y planes together, followed by Cb/Cr/CbCr planes together
            if (i > 0)
            {
                m_planeStartOffset[i] = (m_planeSize[i - 1] * m_numBatchedFrames);
            }
            m_metadataSize[i]         = pFormat->formatParams.yuvFormat[i].metadataSize;
        }

        m_isBacked = TRUE;
        result     = CamxResultSuccess;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::Release
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID ImageBuffer::Release()
{
    UINT referenceCount = GetReferenceCount();

    // We should not be releasing buffers that have a reference to them, but
    // will honor the clients request. Assert is in place to catch this case.
    CAMX_ASSERT(0 == referenceCount);
    if (TRUE == m_isBacked)
    {
        CSLReleaseBuffer(m_hBuffer);
        m_hBuffer = 0;
        Utils::Memset(&m_format, 0, sizeof(m_format));
        Utils::Memset(&m_metadataSize[0], 0, sizeof(m_metadataSize));

        m_pVirtualAddr = NULL;
        m_isBacked     = FALSE;
    }
    else
    {
        CAMX_ASSERT_ALWAYS_MESSAGE("Warning: Release called on an un-backed buffer");
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::AddImageReference
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::AddImageReference()
{
    UINT count = CamxAtomicIncU(&m_aReferenceCount);

    CAMX_LOG_VERBOSE(CamxLogGroupUtils,
                     "ReferenceCount for ImageBuffer %p is being incremented to %d",
                     this, count);

    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::ReleaseImageReference
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::ReleaseImageReference()
{
    CAMX_ASSERT_MESSAGE(0 <= m_aReferenceCount, "Releasing an ImageBuffer with no references");

    UINT count = CamxAtomicDecU(&m_aReferenceCount);

    CAMX_LOG_VERBOSE(CamxLogGroupUtils,
                     "ReferenceCount for ImageBuffer %p is decremented to %d",
                     this, count);

    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::AddBufferManagerImageReference
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::AddBufferManagerImageReference()
{
    UINT count = 0;
    if (NULL != m_pImageBufferManager)
    {
        count = m_pImageBufferManager->AddReference(this);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupUtils, "NULL m_pImageBufferManager");
    }
    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::ReleaseBufferManagerImageReference
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::ReleaseBufferManagerImageReference()
{
    UINT count = 0;
    if (NULL != m_pImageBufferManager)
    {
        count = m_pImageBufferManager->ReleaseReference(this);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupUtils, "NULL m_pImageBufferManager");
    }

    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetReferenceCount
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::GetReferenceCount()
{
    return CamxAtomicLoadU(&m_aReferenceCount);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetFormat
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const ImageFormat* ImageBuffer::GetFormat() const
{
    CAMX_ASSERT(TRUE == m_isBacked);
    if (FALSE == m_isBacked)
    {
        return NULL;
    }

    return &m_format;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetNumberOfPlanes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT ImageBuffer::GetNumberOfPlanes() const
{
    UINT planeCount = 0;
    CAMX_ASSERT(TRUE == m_isBacked);
    if (TRUE == m_isBacked)
    {
        planeCount = ImageFormatUtils::GetNumberOfPlanes(&m_format);
    }

    return planeCount;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetPlaneSize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T ImageBuffer::GetPlaneSize(
    UINT planeIndex
    ) const
{
    SIZE_T      planeSize   = 0;
    const UINT  planeCount  = GetNumberOfPlanes();

    CAMX_ASSERT(TRUE == m_isBacked);
    CAMX_ASSERT(planeIndex < planeCount);
    if ((TRUE == m_isBacked) && (planeIndex < planeCount))
    {
        planeSize = ImageFormatUtils::GetPlaneSize(&m_format, planeIndex);
    }

    return planeSize;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetFileDescriptor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
INT ImageBuffer::GetFileDescriptor() const
{
    return m_fileDescriptor;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetNativeBufferHandle
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* ImageBuffer::GetNativeBufferHandle() const
{
    return (VOID *)m_phNativeBuffer;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetPlaneVirtualAddr
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BYTE* ImageBuffer::GetPlaneVirtualAddr(
    UINT batchFrameIndex,
    UINT planeIndex
    ) const
{
    BYTE*       pVirtualAddr    = NULL;
    const UINT  planeCount      = GetNumberOfPlanes();

    CAMX_ASSERT(TRUE == m_isBacked);
    CAMX_ASSERT(planeIndex < planeCount);
    CAMX_ASSERT(batchFrameIndex < m_numBatchedFrames);

    if ((TRUE == m_isBacked) && (planeIndex < planeCount))
    {
        pVirtualAddr = (m_pVirtualAddr + m_planeStartOffset[planeIndex] + (batchFrameIndex * m_planeSize[planeIndex]));
    }

    return pVirtualAddr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::GetPlaneCSLMemHandle
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ImageBuffer::GetPlaneCSLMemHandle(
    UINT            batchFrameIndex,
    UINT            planeIndex,
    CSLMemHandle*   phMemHandle,
    SIZE_T*         pOffset,
    SIZE_T*         pMetadataSize
    ) const
{
    CamxResult  result      = CamxResultSuccess;
    const UINT  planeCount  = GetNumberOfPlanes();

    CAMX_ASSERT(TRUE == m_isBacked);
    CAMX_ASSERT((NULL != phMemHandle) && (NULL != pOffset) && (NULL != pMetadataSize) && (planeIndex < planeCount));
    CAMX_ASSERT(batchFrameIndex < m_numBatchedFrames);

    if (TRUE != m_isBacked)
    {
        result = CamxResultEInvalidState;
    }
    else if ((NULL == phMemHandle) || (NULL == pOffset) || (NULL == pMetadataSize) || (planeIndex >= planeCount))
    {
        result = CamxResultEInvalidArg;
    }

    if (CamxResultSuccess == result)
    {
        *phMemHandle     = m_hBuffer;
        *pOffset         = (m_planeStartOffset[planeIndex] + (batchFrameIndex * m_planeSize[planeIndex]));
        *pMetadataSize   = m_metadataSize[planeIndex];
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ImageBuffer::CacheOps
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult ImageBuffer::CacheOps(
    BOOL invalidate,
    BOOL clean)
{
    CamxResult result = CamxResultEInvalidState;
    CAMX_ASSERT(TRUE == m_isBacked);
    if (TRUE == m_isBacked)
    {
        CSLBufferCacheOp(m_hBuffer, invalidate, clean);
        result = CamxResultSuccess;
    }
    return result;
}

CAMX_NAMESPACE_END
